This is an R Markdown Notebook with the code to produce an embroidery pattern to stitch your own social network. Work your way through this notebook to generate your own pattern. When you execute code within the notebook, the results appear beneath the code. To execute code chunks, click the Run button within the chunk or by place your cursor inside it and pressing Cmd+Shift+Enter.

DIY Instructions

Get your facebook data

To generate your pattern, you will need your facebook data export. To acquire data:

  1. Visit https://www.facebook.com/dyi while logged in to your facebook account
  2. Select JSON as the format
  3. Select LOW as the media quality (won’t be used but can’t be excluded)
  4. Set the date range to all time
  5. Deselect all options
  6. Set the download to include: Messages, Posts, Comments and reactions, Profile information, Friends and followers
  7. Click generate. This could take awhile (several days), but facebook will notify you when it’s finished.
  8. Save and unzip your download

Get the workspace set up

Set the working directory

For this notebook to execture correctly, the working directory needs to be set to the parent folder of your unzipped facebook export. To do this, either:

  • manually set the working directory using the “Session -> set working directory” menu item
  • save the notebook to the parent folder and then run the following code chunk
current_path = rstudioapi::getActiveDocumentContext()$path 
setwd(dirname(current_path ))
print(paste(c("Your working directory is now ", dirname(current_path )),collapse = ""))
[1] "Your working directory is now /Users/katieanderson-kelly/Study/DESN6003/assignment3/facebook-kandersonkelly"

Load R libraries

Load the R libraries that will be used throughout the script

rr library(jsonlite) # for working with the JSON files library(anytime) # for converting timestamps library(tidyverse) library(dplyr) library(ggplot2)

Generate pattern: Part A

Get your list of friends

The first step of generating the pattern is reading in and processing your list of facebook friends. This code chunk reads in the “friends_and_followers” JSON file.

friendsFollowers <- fromJSON("friends_and_followers/friends.json") # read in file
friendsFollowers <- friendsFollowers$friends_v2 #keep only the useful part of the json object
friendsFollowers$connectedDate <- anytime(friendsFollowers$timestamp) #convert the timestamp from epoch time to local time (workspace default)
friendsFollowers <- friendsFollowers %>% arrange(timestamp)
friendsFollowers$rankOrder <- 1:nrow(friendsFollowers) #create a rank order variable

Write out friends csv

Export a list of your facebook friends as a csv so that you can identify the ones your want highlighted in your pattern.

rr friendsList <- friendsFollowers %>% select(rankOrder, name) %>% add_column(highlight = , annotation = \, group = \) write_csv(friendsList, .csv) #Export the ordered list of friends

Choose who to highlight

Open the friends.csv created in the previous step. To select friends who should be highlighted in the pattern:

  • change the “N” in the highlight column to a “Y”.
  • (Optional): add any notes you’d like included in the “annotation” column
  • (Optional): assign people to a single group by listing the group name in the group column. This can be used later to colour your points in the pattern.

Generate pattern: Part B

Process the interaction data

#Initialise a blank dataframe to store message metadata
messages <- data.frame(sender_name=character(), timestamp_ms=numeric(), type=character()) 

## Getting and processing inbox message data
appendMessages <- function(filename) { # function needed for processing the inbox messages
  tempDF <- fromJSON(filename) # read in file
  tempDF <- tempDF$messages #keep only the useful part of the json object
  tempDF <- tempDF  %>% select(sender_name, timestamp_ms) #keep only useful columns
  messages <<- bind_rows(messages,tempDF) #push the messages from this conversation into the main messages folder
}

#Read in a list of all the subfolders within the message/inbox directory
inboxFolders <- list.files("messages/inbox")
# Loop through the inbox message subfolders and push the messages into the main message dataframe
for (i in 1:length(inboxFolders)) {
  appendMessages(paste(c("messages/inbox/",inboxFolders[[i]],"/message_1.json"),collapse=""))
}

#Summarise the messages dataset by sender name, to get the total count and most recent
messagesSummary <- messages %>%
  group_by(sender_name) %>%
  summarise(countM = n(), mostRecentM = max(timestamp_ms))
#Join this to the existing friends data
interactions <- left_join(friendsFollowers,messagesSummary, by=c("name"="sender_name"))
selectedFriends <- read_csv("friends.csv")
interactions <- left_join(interactions,selectedFriends, by=c("name"="name", "rankOrder" = "rankOrder"))

Set up your pattern requirements

Edit any of the below parameters to adjust your desired pattern size.

hoopSize <- 18 #Embroidery hoop diameter (cm)
hoopRadius <- hoopSize/2 
innerBuffer <- .5 #Size of the inner buffer in the centre of the pattern (radius)
outerBuffer <- 1.5 #Size of the outer buffer in the centre of the pattern (radius)
circleSize <- .8 #Size of the points in the pattern
pageW <- 21 #Page width (cm)
pageH <- 29.7 #Page height (cm)

Set up your pattern colours

By adding group names to the csv, you can choose to add colours to your pattern. List the group names and desired colour codes here. Use the format: ‘groupname’=‘#colour hex value’

groupColours <- c(
'Family'='#501737',
'School'='#105495',
'University'='#269788',
'Capoeira'='#e28738',
'Work'='#9e1e08',
'Other'='#565662')

Generate and export the pattern

Running the following code chunk will generate the pattern and export it as a PDF.


#Process the message data
interactions$mostRecentMDate <- anytime(interactions$mostRecentM/1000) # Get the most recent message date as formatted date time
interactions$recentMDiff <- round(as.numeric(difftime(Sys.Date(),interactions$mostRecentMDate,units = "days")),0) # Difference between most recent message and now
interactions$connectedDiff <- round(as.numeric(difftime(Sys.Date(),interactions$connectedDate,units = "days")),0) # Difference between when connected and now
interactions$recentMNormalised <- interactions$recentMDiff/interactions$connectedDiff # Adjust the recent message time as a proportion of the time connected
interactions$recentMNormalised <- ifelse(interactions$recentMNormalised > 1, 1, interactions$recentMNormalised)  #Replace any messages from before connection with 1
interactions <- dplyr::mutate(interactions, across(c(countM), replace_na, 0)) # Replace any missing values with 0
interactions <- dplyr::mutate(interactions, across(c(recentMNormalised), replace_na, 1)) # Replace any missing values with 1
interactions$countMratio <- abs(interactions$countM / interactions$connectedDiff) # Get the ratio of number of messages to number of days connected
maxCountMRatio <- max(interactions$countMratio) # Get the max ratio for all friends
interactions$countMratioNormalised <- 1-interactions$countMratio/maxCountMRatio # Normalise the frequency ratio across all friends by this max

#Calculate the position of each point
interactions <- interactions %>% 
  mutate(
    distance_to_center = (countMratioNormalised*1+(recentMNormalised*1.0))/2 * (hoopRadius - outerBuffer-innerBuffer) + innerBuffer, # position based on a weighted combination of both
    direction = rankOrder*(2*pi)/nrow(interactions),
    x = distance_to_center*sin(direction),
    y = distance_to_center*cos(direction))

#Draw the pattern
pattern <- ggplot(interactions, aes(x, y, shape=highlight)) + geom_point(size = circleSize,  aes(color=group )) +
  theme(aspect.ratio=sqrt(2)/1) + 
  scale_color_manual(values = groupColours) +
  scale_shape_manual(values = c("Y"=1, "N" = 19)) +
  xlim(-pageW/2, pageW/2) + 
  ylim(pageH/3*-2,pageH/3) +
 ## annotate("path", x=hoopRadius*cos(seq(0,2*pi,length.out=100)), y=hoopRadius*sin(seq(0,2*pi,length.out=100)), color="lightgrey")+ ##comment out if external circle not desired
  annotate(geom = "point", x = 0, y = 0, color = "lightgrey") + 
  annotate("text", x = -1*pageW/3, y = pageH/3, label = "This way up", size=3,hjust = 0) +
  theme(axis.line=element_blank(),axis.text.x=element_blank(),
        axis.text.y=element_blank(),axis.ticks=element_blank(),
        axis.title.x=element_blank(),
        axis.title.y=element_blank(),legend.position="none",
        panel.background=element_blank(),panel.border=element_blank(),panel.grid.major=element_blank(),
        panel.grid.minor=element_blank(),plot.background=element_blank()) + 
  theme(legend.position = c(0.38, 0.15), 
        legend.text=element_text(size=8),
        legend.direction="horizontal", 
        legend.key=element_blank(),
        legend.title=element_text(size=10), 
        legend.background=element_blank()) +
  guides(colour = guide_legend(override.aes = list(size=2), nrow=1), shape = 'none')+ 
  labs(color='') +
  annotate("text", x = pageW/3*-1.3, y = pageH/3*-1.25, label = "Legend", size=3.5, hjust = 0)+
  annotate("text", x = pageW/3*-1.3, y = pageH/3*-1.33, label = "Position", size=3, hjust = 0) +
  annotate("text", x = pageW/3*-1.3, y = pageH/3*-1.4, label = "Friends are arranged sequentially in the order the facebook connections were made, \nclockwise from oldest to newest.", size=2.6, hjust = 0)+
  annotate("text", x = pageW/3*-1.3, y = pageH/3*-1.5, label = "Distance from the centrepoint is determined by how requently/frequently the friend has \nmessaged you on facebook (with most recent/frequent closer to the centre.)", size=2.6, hjust = 0) +
  annotate("text", x = pageW/3*-1.3, y = pageH/3*-1.59, label = "Met through", size=3, hjust = 0) +
  annotate("text", x = pageW/3*-1.3, y = pageH/3*-1.72, label = "Connections", size=3, hjust = 0) 

ggsave("pattern.pdf", width = pageW+1,  height = pageH+1,  units = "cm") ## Export the pattern as a PDF

library(egg)

#ggsave('pattern.pdf', egg::set_panel_size(pattern, width=unit(pageW, "cm"), height=unit(pageH, "cm")), 
#       width = pageW, height = pageH, units = 'cm', dpi = 300)

Embroider

You can use this interactive reference as a guide while embroidering your pattern.

## Handle if group colours aren't in use - comment out if they are, remove # if they aren't
#groupColours <- c(groupColours,'None'='#7d7d7d')
#interactions <- dplyr::mutate(interactions, across(c(group), replace_na, 'None')) #Replace blank groups with 'none'

interactions$name <- "redacted" ##Uncomment this line to redact names from HTML save if needed.

## Draw interactive plot
fig <- plot_ly(
  interactions, x = ~x, y = ~y, width=700, height=700, type = 'scatter', mode = 'markers', color = ~group, colors = groupColours,
  symbol = ~highlight, symbols = c("Y"='circle', "N" = 'o'),
  # Hover text:
  text = ~paste("Name: ", name,"<br>Met because:", group, "<br>First connected:", connectedDate, "<br>Most recent message:",mostRecentMDate, "<br># messages:", countM, "<br>Annotation:", annotation)) %>% layout(showlegend = FALSE)

fig

Note: The filled/stroked dots are inverted between the print and interactive versions.

Share

Post a photo of your finished piece to instagram or facebook using #stitchyoursocialnetwork

LS0tCnRpdGxlOiAiU3RpdGNoIHlvdXIgc29jaWFsIG5ldHdvcmsiCm91dHB1dDogaHRtbF9ub3RlYm9vawotLS0KClRoaXMgaXMgYW4gW1IgTWFya2Rvd25dKGh0dHA6Ly9ybWFya2Rvd24ucnN0dWRpby5jb20pIE5vdGVib29rIHdpdGggdGhlIGNvZGUgdG8gcHJvZHVjZSBhbiBlbWJyb2lkZXJ5IHBhdHRlcm4gdG8gc3RpdGNoIHlvdXIgb3duIHNvY2lhbCBuZXR3b3JrLiBXb3JrIHlvdXIgd2F5IHRocm91Z2ggdGhpcyBub3RlYm9vayB0byBnZW5lcmF0ZSB5b3VyIG93biBwYXR0ZXJuLiBXaGVuIHlvdSBleGVjdXRlIGNvZGUgd2l0aGluIHRoZSBub3RlYm9vaywgdGhlIHJlc3VsdHMgYXBwZWFyIGJlbmVhdGggdGhlIGNvZGUuIFRvIGV4ZWN1dGUgY29kZSBjaHVua3MsIGNsaWNrIHRoZSAqUnVuKiBidXR0b24gd2l0aGluIHRoZSBjaHVuayBvciBieSBwbGFjZSB5b3VyIGN1cnNvciBpbnNpZGUgaXQgYW5kIHByZXNzaW5nICpDbWQrU2hpZnQrRW50ZXIqLiAKCgojIERJWSBJbnN0cnVjdGlvbnMKCgojIyBHZXQgeW91ciBmYWNlYm9vayBkYXRhClRvIGdlbmVyYXRlIHlvdXIgcGF0dGVybiwgeW91IHdpbGwgbmVlZCB5b3VyIGZhY2Vib29rIGRhdGEgZXhwb3J0LiBUbyBhY3F1aXJlIGRhdGE6CgoxLiBWaXNpdCBbaHR0cHM6Ly93d3cuZmFjZWJvb2suY29tL2R5aV0oaHR0cHM6Ly93d3cuZmFjZWJvb2suY29tL2R5aSkgd2hpbGUgbG9nZ2VkIGluIHRvIHlvdXIgZmFjZWJvb2sgYWNjb3VudAoyLiBTZWxlY3QgSlNPTiBhcyB0aGUgZm9ybWF0CjMuIFNlbGVjdCBMT1cgYXMgdGhlIG1lZGlhIHF1YWxpdHkgKHdvbid0IGJlIHVzZWQgYnV0IGNhbid0IGJlIGV4Y2x1ZGVkKQo0LiBTZXQgdGhlIGRhdGUgcmFuZ2UgdG8gYWxsIHRpbWUKNS4gRGVzZWxlY3QgYWxsIG9wdGlvbnMKNi4gU2V0IHRoZSBkb3dubG9hZCB0byBpbmNsdWRlOiBNZXNzYWdlcywgUG9zdHMsIENvbW1lbnRzIGFuZCByZWFjdGlvbnMsIFByb2ZpbGUgaW5mb3JtYXRpb24sIEZyaWVuZHMgYW5kIGZvbGxvd2Vycwo3LiBDbGljayBnZW5lcmF0ZS4gVGhpcyBjb3VsZCB0YWtlIGF3aGlsZSAoc2V2ZXJhbCBkYXlzKSwgYnV0IGZhY2Vib29rIHdpbGwgbm90aWZ5IHlvdSB3aGVuIGl0J3MgZmluaXNoZWQuCjguIFNhdmUgYW5kIHVuemlwIHlvdXIgZG93bmxvYWQKCiMjIEdldCB0aGUgd29ya3NwYWNlIHNldCB1cAoKIyMjIFNldCB0aGUgd29ya2luZyBkaXJlY3RvcnkKRm9yIHRoaXMgbm90ZWJvb2sgdG8gZXhlY3R1cmUgY29ycmVjdGx5LCB0aGUgd29ya2luZyBkaXJlY3RvcnkgbmVlZHMgdG8gYmUgc2V0IHRvIHRoZSBwYXJlbnQgZm9sZGVyIG9mIHlvdXIgdW56aXBwZWQgZmFjZWJvb2sgZXhwb3J0LiBUbyBkbyB0aGlzLCBlaXRoZXI6CgoqIG1hbnVhbGx5IHNldCB0aGUgd29ya2luZyBkaXJlY3RvcnkgdXNpbmcgdGhlICJTZXNzaW9uIC0+IHNldCB3b3JraW5nIGRpcmVjdG9yeSIgbWVudSBpdGVtCiogc2F2ZSB0aGUgbm90ZWJvb2sgdG8gdGhlIHBhcmVudCBmb2xkZXIgYW5kIHRoZW4gcnVuIHRoZSBmb2xsb3dpbmcgY29kZSBjaHVuawpgYGB7cn0KY3VycmVudF9wYXRoID0gcnN0dWRpb2FwaTo6Z2V0QWN0aXZlRG9jdW1lbnRDb250ZXh0KCkkcGF0aCAKc2V0d2QoZGlybmFtZShjdXJyZW50X3BhdGggKSkKcHJpbnQocGFzdGUoYygiWW91ciB3b3JraW5nIGRpcmVjdG9yeSBpcyBub3cgIiwgZGlybmFtZShjdXJyZW50X3BhdGggKSksY29sbGFwc2UgPSAiIikpCmBgYAoKIyMjIExvYWQgUiBsaWJyYXJpZXMKTG9hZCB0aGUgUiBsaWJyYXJpZXMgdGhhdCB3aWxsIGJlIHVzZWQgdGhyb3VnaG91dCB0aGUgc2NyaXB0CmBgYHtyfQpsaWJyYXJ5KGpzb25saXRlKSAjIGZvciB3b3JraW5nIHdpdGggdGhlIEpTT04gZmlsZXMKbGlicmFyeShhbnl0aW1lKSAjIGZvciBjb252ZXJ0aW5nIHRpbWVzdGFtcHMKbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkoZHBseXIpCmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeShwbG90bHkpCmBgYAoKCiMjIEdlbmVyYXRlIHBhdHRlcm46IFBhcnQgQQoKIyMjIEdldCB5b3VyIGxpc3Qgb2YgZnJpZW5kcwpUaGUgZmlyc3Qgc3RlcCBvZiBnZW5lcmF0aW5nIHRoZSBwYXR0ZXJuIGlzIHJlYWRpbmcgaW4gYW5kIHByb2Nlc3NpbmcgeW91ciBsaXN0IG9mIGZhY2Vib29rIGZyaWVuZHMuIFRoaXMgY29kZSBjaHVuayByZWFkcyBpbiB0aGUgImZyaWVuZHNfYW5kX2ZvbGxvd2VycyIgSlNPTiBmaWxlLgpgYGB7cn0KZnJpZW5kc0ZvbGxvd2VycyA8LSBmcm9tSlNPTigiZnJpZW5kc19hbmRfZm9sbG93ZXJzL2ZyaWVuZHMuanNvbiIpICMgcmVhZCBpbiBmaWxlCmZyaWVuZHNGb2xsb3dlcnMgPC0gZnJpZW5kc0ZvbGxvd2VycyRmcmllbmRzX3YyICNrZWVwIG9ubHkgdGhlIHVzZWZ1bCBwYXJ0IG9mIHRoZSBqc29uIG9iamVjdApmcmllbmRzRm9sbG93ZXJzJGNvbm5lY3RlZERhdGUgPC0gYW55dGltZShmcmllbmRzRm9sbG93ZXJzJHRpbWVzdGFtcCkgI2NvbnZlcnQgdGhlIHRpbWVzdGFtcCBmcm9tIGVwb2NoIHRpbWUgdG8gbG9jYWwgdGltZSAod29ya3NwYWNlIGRlZmF1bHQpCmZyaWVuZHNGb2xsb3dlcnMgPC0gZnJpZW5kc0ZvbGxvd2VycyAlPiUgYXJyYW5nZSh0aW1lc3RhbXApCmZyaWVuZHNGb2xsb3dlcnMkcmFua09yZGVyIDwtIDE6bnJvdyhmcmllbmRzRm9sbG93ZXJzKSAjY3JlYXRlIGEgcmFuayBvcmRlciB2YXJpYWJsZQpgYGAKCgojIyMgV3JpdGUgb3V0IGZyaWVuZHMgY3N2CkV4cG9ydCBhIGxpc3Qgb2YgeW91ciBmYWNlYm9vayBmcmllbmRzIGFzIGEgY3N2IHNvIHRoYXQgeW91IGNhbiBpZGVudGlmeSB0aGUgb25lcyB5b3VyIHdhbnQgaGlnaGxpZ2h0ZWQgaW4geW91ciBwYXR0ZXJuLgpgYGB7cn0KZnJpZW5kc0xpc3QgPC0gZnJpZW5kc0ZvbGxvd2VycyAlPiUgc2VsZWN0KHJhbmtPcmRlciwgbmFtZSkgJT4lCiAgYWRkX2NvbHVtbihoaWdobGlnaHQgPSAiTiIsIGFubm90YXRpb24gPSAiIiwgZ3JvdXAgPSAiIikKd3JpdGVfY3N2KGZyaWVuZHNMaXN0LCAiZnJpZW5kcy5jc3YiKSAjRXhwb3J0IHRoZSBvcmRlcmVkIGxpc3Qgb2YgZnJpZW5kcwpgYGAKCiMjIyBDaG9vc2Ugd2hvIHRvIGhpZ2hsaWdodApPcGVuIHRoZSBmcmllbmRzLmNzdiBjcmVhdGVkIGluIHRoZSBwcmV2aW91cyBzdGVwLiBUbyBzZWxlY3QgZnJpZW5kcyB3aG8gc2hvdWxkIGJlIGhpZ2hsaWdodGVkIGluIHRoZSBwYXR0ZXJuOgoKKiBjaGFuZ2UgdGhlICJOIiBpbiB0aGUgaGlnaGxpZ2h0IGNvbHVtbiB0byBhICJZIi4gCiogKE9wdGlvbmFsKTogYWRkIGFueSBub3RlcyB5b3UnZCBsaWtlIGluY2x1ZGVkIGluIHRoZSAiYW5ub3RhdGlvbiIgY29sdW1uCiogKE9wdGlvbmFsKTogYXNzaWduIHBlb3BsZSB0byBhIHNpbmdsZSBncm91cCBieSBsaXN0aW5nIHRoZSBncm91cCBuYW1lIGluIHRoZSBncm91cCBjb2x1bW4uIFRoaXMgY2FuIGJlIHVzZWQgbGF0ZXIgdG8gY29sb3VyIHlvdXIgcG9pbnRzIGluIHRoZSBwYXR0ZXJuLiAKCiMjIEdlbmVyYXRlIHBhdHRlcm46IFBhcnQgQgoKIyMjIFByb2Nlc3MgdGhlIGludGVyYWN0aW9uIGRhdGEKYGBge3J9CiNJbml0aWFsaXNlIGEgYmxhbmsgZGF0YWZyYW1lIHRvIHN0b3JlIG1lc3NhZ2UgbWV0YWRhdGEKbWVzc2FnZXMgPC0gZGF0YS5mcmFtZShzZW5kZXJfbmFtZT1jaGFyYWN0ZXIoKSwgdGltZXN0YW1wX21zPW51bWVyaWMoKSwgdHlwZT1jaGFyYWN0ZXIoKSkgCgojIyBHZXR0aW5nIGFuZCBwcm9jZXNzaW5nIGluYm94IG1lc3NhZ2UgZGF0YQphcHBlbmRNZXNzYWdlcyA8LSBmdW5jdGlvbihmaWxlbmFtZSkgeyAjIGZ1bmN0aW9uIG5lZWRlZCBmb3IgcHJvY2Vzc2luZyB0aGUgaW5ib3ggbWVzc2FnZXMKICB0ZW1wREYgPC0gZnJvbUpTT04oZmlsZW5hbWUpICMgcmVhZCBpbiBmaWxlCiAgdGVtcERGIDwtIHRlbXBERiRtZXNzYWdlcyAja2VlcCBvbmx5IHRoZSB1c2VmdWwgcGFydCBvZiB0aGUganNvbiBvYmplY3QKICB0ZW1wREYgPC0gdGVtcERGICAlPiUgc2VsZWN0KHNlbmRlcl9uYW1lLCB0aW1lc3RhbXBfbXMpICNrZWVwIG9ubHkgdXNlZnVsIGNvbHVtbnMKICBtZXNzYWdlcyA8PC0gYmluZF9yb3dzKG1lc3NhZ2VzLHRlbXBERikgI3B1c2ggdGhlIG1lc3NhZ2VzIGZyb20gdGhpcyBjb252ZXJzYXRpb24gaW50byB0aGUgbWFpbiBtZXNzYWdlcyBmb2xkZXIKfQoKI1JlYWQgaW4gYSBsaXN0IG9mIGFsbCB0aGUgc3ViZm9sZGVycyB3aXRoaW4gdGhlIG1lc3NhZ2UvaW5ib3ggZGlyZWN0b3J5CmluYm94Rm9sZGVycyA8LSBsaXN0LmZpbGVzKCJtZXNzYWdlcy9pbmJveCIpCiMgTG9vcCB0aHJvdWdoIHRoZSBpbmJveCBtZXNzYWdlIHN1YmZvbGRlcnMgYW5kIHB1c2ggdGhlIG1lc3NhZ2VzIGludG8gdGhlIG1haW4gbWVzc2FnZSBkYXRhZnJhbWUKZm9yIChpIGluIDE6bGVuZ3RoKGluYm94Rm9sZGVycykpIHsKICBhcHBlbmRNZXNzYWdlcyhwYXN0ZShjKCJtZXNzYWdlcy9pbmJveC8iLGluYm94Rm9sZGVyc1tbaV1dLCIvbWVzc2FnZV8xLmpzb24iKSxjb2xsYXBzZT0iIikpCn0KCiNTdW1tYXJpc2UgdGhlIG1lc3NhZ2VzIGRhdGFzZXQgYnkgc2VuZGVyIG5hbWUsIHRvIGdldCB0aGUgdG90YWwgY291bnQgYW5kIG1vc3QgcmVjZW50Cm1lc3NhZ2VzU3VtbWFyeSA8LSBtZXNzYWdlcyAlPiUKICBncm91cF9ieShzZW5kZXJfbmFtZSkgJT4lCiAgc3VtbWFyaXNlKGNvdW50TSA9IG4oKSwgbW9zdFJlY2VudE0gPSBtYXgodGltZXN0YW1wX21zKSkKCmBgYAoKCmBgYHtyfQojSm9pbiB0aGlzIHRvIHRoZSBleGlzdGluZyBmcmllbmRzIGRhdGEKaW50ZXJhY3Rpb25zIDwtIGxlZnRfam9pbihmcmllbmRzRm9sbG93ZXJzLG1lc3NhZ2VzU3VtbWFyeSwgYnk9YygibmFtZSI9InNlbmRlcl9uYW1lIikpCnNlbGVjdGVkRnJpZW5kcyA8LSByZWFkX2NzdigiZnJpZW5kcy5jc3YiKQppbnRlcmFjdGlvbnMgPC0gbGVmdF9qb2luKGludGVyYWN0aW9ucyxzZWxlY3RlZEZyaWVuZHMsIGJ5PWMoIm5hbWUiPSJuYW1lIiwgInJhbmtPcmRlciIgPSAicmFua09yZGVyIikpCmBgYAoKCiMjIyBTZXQgdXAgeW91ciBwYXR0ZXJuIHJlcXVpcmVtZW50cwpFZGl0IGFueSBvZiB0aGUgYmVsb3cgcGFyYW1ldGVycyB0byBhZGp1c3QgeW91ciBkZXNpcmVkIHBhdHRlcm4gc2l6ZS4gCmBgYHtyfQpob29wU2l6ZSA8LSAxOCAjRW1icm9pZGVyeSBob29wIGRpYW1ldGVyIChjbSkKaG9vcFJhZGl1cyA8LSBob29wU2l6ZS8yIAppbm5lckJ1ZmZlciA8LSAuNSAjU2l6ZSBvZiB0aGUgaW5uZXIgYnVmZmVyIGluIHRoZSBjZW50cmUgb2YgdGhlIHBhdHRlcm4gKHJhZGl1cykKb3V0ZXJCdWZmZXIgPC0gMS41ICNTaXplIG9mIHRoZSBvdXRlciBidWZmZXIgaW4gdGhlIGNlbnRyZSBvZiB0aGUgcGF0dGVybiAocmFkaXVzKQpjaXJjbGVTaXplIDwtIC44ICNTaXplIG9mIHRoZSBwb2ludHMgaW4gdGhlIHBhdHRlcm4KcGFnZVcgPC0gMjEgI1BhZ2Ugd2lkdGggKGNtKQpwYWdlSCA8LSAyOS43ICNQYWdlIGhlaWdodCAoY20pCmBgYAoKIyMjIFNldCB1cCB5b3VyIHBhdHRlcm4gY29sb3VycwpCeSBhZGRpbmcgZ3JvdXAgbmFtZXMgdG8gdGhlIGNzdiwgeW91IGNhbiBjaG9vc2UgdG8gYWRkIGNvbG91cnMgdG8geW91ciBwYXR0ZXJuLiBMaXN0IHRoZSBncm91cCBuYW1lcyBhbmQgZGVzaXJlZCBjb2xvdXIgY29kZXMgaGVyZS4KVXNlIHRoZSBmb3JtYXQ6ICdncm91cG5hbWUnPScjY29sb3VyIGhleCB2YWx1ZScKYGBge3J9Cmdyb3VwQ29sb3VycyA8LSBjKAonRmFtaWx5Jz0nIzUwMTczNycsCidTY2hvb2wnPScjMTA1NDk1JywKJ1VuaXZlcnNpdHknPScjMjY5Nzg4JywKJ0NhcG9laXJhJz0nI2UyODczOCcsCidXb3JrJz0nIzllMWUwOCcsCidPdGhlcic9JyM1NjU2NjInKQpgYGAKCgojIyMgR2VuZXJhdGUgYW5kIGV4cG9ydCB0aGUgcGF0dGVybgpSdW5uaW5nIHRoZSBmb2xsb3dpbmcgY29kZSBjaHVuayB3aWxsIGdlbmVyYXRlIHRoZSBwYXR0ZXJuIGFuZCBleHBvcnQgaXQgYXMgYSBQREYuIApgYGB7cn0KCiNQcm9jZXNzIHRoZSBtZXNzYWdlIGRhdGEKaW50ZXJhY3Rpb25zJG1vc3RSZWNlbnRNRGF0ZSA8LSBhbnl0aW1lKGludGVyYWN0aW9ucyRtb3N0UmVjZW50TS8xMDAwKSAjIEdldCB0aGUgbW9zdCByZWNlbnQgbWVzc2FnZSBkYXRlIGFzIGZvcm1hdHRlZCBkYXRlIHRpbWUKaW50ZXJhY3Rpb25zJHJlY2VudE1EaWZmIDwtIHJvdW5kKGFzLm51bWVyaWMoZGlmZnRpbWUoU3lzLkRhdGUoKSxpbnRlcmFjdGlvbnMkbW9zdFJlY2VudE1EYXRlLHVuaXRzID0gImRheXMiKSksMCkgIyBEaWZmZXJlbmNlIGJldHdlZW4gbW9zdCByZWNlbnQgbWVzc2FnZSBhbmQgbm93CmludGVyYWN0aW9ucyRjb25uZWN0ZWREaWZmIDwtIHJvdW5kKGFzLm51bWVyaWMoZGlmZnRpbWUoU3lzLkRhdGUoKSxpbnRlcmFjdGlvbnMkY29ubmVjdGVkRGF0ZSx1bml0cyA9ICJkYXlzIikpLDApICMgRGlmZmVyZW5jZSBiZXR3ZWVuIHdoZW4gY29ubmVjdGVkIGFuZCBub3cKaW50ZXJhY3Rpb25zJHJlY2VudE1Ob3JtYWxpc2VkIDwtIGludGVyYWN0aW9ucyRyZWNlbnRNRGlmZi9pbnRlcmFjdGlvbnMkY29ubmVjdGVkRGlmZiAjIEFkanVzdCB0aGUgcmVjZW50IG1lc3NhZ2UgdGltZSBhcyBhIHByb3BvcnRpb24gb2YgdGhlIHRpbWUgY29ubmVjdGVkCmludGVyYWN0aW9ucyRyZWNlbnRNTm9ybWFsaXNlZCA8LSBpZmVsc2UoaW50ZXJhY3Rpb25zJHJlY2VudE1Ob3JtYWxpc2VkID4gMSwgMSwgaW50ZXJhY3Rpb25zJHJlY2VudE1Ob3JtYWxpc2VkKSAgI1JlcGxhY2UgYW55IG1lc3NhZ2VzIGZyb20gYmVmb3JlIGNvbm5lY3Rpb24gd2l0aCAxCmludGVyYWN0aW9ucyA8LSBkcGx5cjo6bXV0YXRlKGludGVyYWN0aW9ucywgYWNyb3NzKGMoY291bnRNKSwgcmVwbGFjZV9uYSwgMCkpICMgUmVwbGFjZSBhbnkgbWlzc2luZyB2YWx1ZXMgd2l0aCAwCmludGVyYWN0aW9ucyA8LSBkcGx5cjo6bXV0YXRlKGludGVyYWN0aW9ucywgYWNyb3NzKGMocmVjZW50TU5vcm1hbGlzZWQpLCByZXBsYWNlX25hLCAxKSkgIyBSZXBsYWNlIGFueSBtaXNzaW5nIHZhbHVlcyB3aXRoIDEKaW50ZXJhY3Rpb25zJGNvdW50TXJhdGlvIDwtIGFicyhpbnRlcmFjdGlvbnMkY291bnRNIC8gaW50ZXJhY3Rpb25zJGNvbm5lY3RlZERpZmYpICMgR2V0IHRoZSByYXRpbyBvZiBudW1iZXIgb2YgbWVzc2FnZXMgdG8gbnVtYmVyIG9mIGRheXMgY29ubmVjdGVkCm1heENvdW50TVJhdGlvIDwtIG1heChpbnRlcmFjdGlvbnMkY291bnRNcmF0aW8pICMgR2V0IHRoZSBtYXggcmF0aW8gZm9yIGFsbCBmcmllbmRzCmludGVyYWN0aW9ucyRjb3VudE1yYXRpb05vcm1hbGlzZWQgPC0gMS1pbnRlcmFjdGlvbnMkY291bnRNcmF0aW8vbWF4Q291bnRNUmF0aW8gIyBOb3JtYWxpc2UgdGhlIGZyZXF1ZW5jeSByYXRpbyBhY3Jvc3MgYWxsIGZyaWVuZHMgYnkgdGhpcyBtYXgKCiNDYWxjdWxhdGUgdGhlIHBvc2l0aW9uIG9mIGVhY2ggcG9pbnQKaW50ZXJhY3Rpb25zIDwtIGludGVyYWN0aW9ucyAlPiUgCiAgbXV0YXRlKAogICAgZGlzdGFuY2VfdG9fY2VudGVyID0gKGNvdW50TXJhdGlvTm9ybWFsaXNlZCoxKyhyZWNlbnRNTm9ybWFsaXNlZCoxLjApKS8yICogKGhvb3BSYWRpdXMgLSBvdXRlckJ1ZmZlci1pbm5lckJ1ZmZlcikgKyBpbm5lckJ1ZmZlciwgIyBwb3NpdGlvbiBiYXNlZCBvbiBhIHdlaWdodGVkIGNvbWJpbmF0aW9uIG9mIGJvdGgKICAgIGRpcmVjdGlvbiA9IHJhbmtPcmRlciooMipwaSkvbnJvdyhpbnRlcmFjdGlvbnMpLAogICAgeCA9IGRpc3RhbmNlX3RvX2NlbnRlcipzaW4oZGlyZWN0aW9uKSwKICAgIHkgPSBkaXN0YW5jZV90b19jZW50ZXIqY29zKGRpcmVjdGlvbikpCgojRHJhdyB0aGUgcGF0dGVybgpwYXR0ZXJuIDwtIGdncGxvdChpbnRlcmFjdGlvbnMsIGFlcyh4LCB5LCBzaGFwZT1oaWdobGlnaHQpKSArIGdlb21fcG9pbnQoc2l6ZSA9IGNpcmNsZVNpemUsICBhZXMoY29sb3I9Z3JvdXAgKSkgKwogIHRoZW1lKGFzcGVjdC5yYXRpbz1zcXJ0KDIpLzEpICsgCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGdyb3VwQ29sb3VycykgKwogIHNjYWxlX3NoYXBlX21hbnVhbCh2YWx1ZXMgPSBjKCJZIj0xLCAiTiIgPSAxOSkpICsKICB4bGltKC1wYWdlVy8yLCBwYWdlVy8yKSArIAogIHlsaW0ocGFnZUgvMyotMixwYWdlSC8zKSArCiAjIyBhbm5vdGF0ZSgicGF0aCIsIHg9aG9vcFJhZGl1cypjb3Moc2VxKDAsMipwaSxsZW5ndGgub3V0PTEwMCkpLCB5PWhvb3BSYWRpdXMqc2luKHNlcSgwLDIqcGksbGVuZ3RoLm91dD0xMDApKSwgY29sb3I9ImxpZ2h0Z3JleSIpKyAjI2NvbW1lbnQgb3V0IGlmIGV4dGVybmFsIGNpcmNsZSBub3QgZGVzaXJlZAogIGFubm90YXRlKGdlb20gPSAicG9pbnQiLCB4ID0gMCwgeSA9IDAsIGNvbG9yID0gImxpZ2h0Z3JleSIpICsgCiAgYW5ub3RhdGUoInRleHQiLCB4ID0gLTEqcGFnZVcvMywgeSA9IHBhZ2VILzMsIGxhYmVsID0gIlRoaXMgd2F5IHVwIiwgc2l6ZT0zLGhqdXN0ID0gMCkgKwogIHRoZW1lKGF4aXMubGluZT1lbGVtZW50X2JsYW5rKCksYXhpcy50ZXh0Lng9ZWxlbWVudF9ibGFuaygpLAogICAgICAgIGF4aXMudGV4dC55PWVsZW1lbnRfYmxhbmsoKSxheGlzLnRpY2tzPWVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBheGlzLnRpdGxlLng9ZWxlbWVudF9ibGFuaygpLAogICAgICAgIGF4aXMudGl0bGUueT1lbGVtZW50X2JsYW5rKCksbGVnZW5kLnBvc2l0aW9uPSJub25lIiwKICAgICAgICBwYW5lbC5iYWNrZ3JvdW5kPWVsZW1lbnRfYmxhbmsoKSxwYW5lbC5ib3JkZXI9ZWxlbWVudF9ibGFuaygpLHBhbmVsLmdyaWQubWFqb3I9ZWxlbWVudF9ibGFuaygpLAogICAgICAgIHBhbmVsLmdyaWQubWlub3I9ZWxlbWVudF9ibGFuaygpLHBsb3QuYmFja2dyb3VuZD1lbGVtZW50X2JsYW5rKCkpICsgCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gYygwLjM4LCAwLjE1KSwgCiAgICAgICAgbGVnZW5kLnRleHQ9ZWxlbWVudF90ZXh0KHNpemU9OCksCiAgICAgICAgbGVnZW5kLmRpcmVjdGlvbj0iaG9yaXpvbnRhbCIsIAogICAgICAgIGxlZ2VuZC5rZXk9ZWxlbWVudF9ibGFuaygpLAogICAgICAgIGxlZ2VuZC50aXRsZT1lbGVtZW50X3RleHQoc2l6ZT0xMCksIAogICAgICAgIGxlZ2VuZC5iYWNrZ3JvdW5kPWVsZW1lbnRfYmxhbmsoKSkgKwogIGd1aWRlcyhjb2xvdXIgPSBndWlkZV9sZWdlbmQob3ZlcnJpZGUuYWVzID0gbGlzdChzaXplPTIpLCBucm93PTEpLCBzaGFwZSA9ICdub25lJykrIAogIGxhYnMoY29sb3I9JycpICsKICBhbm5vdGF0ZSgidGV4dCIsIHggPSBwYWdlVy8zKi0xLjMsIHkgPSBwYWdlSC8zKi0xLjI1LCBsYWJlbCA9ICJMZWdlbmQiLCBzaXplPTMuNSwgaGp1c3QgPSAwKSsKICBhbm5vdGF0ZSgidGV4dCIsIHggPSBwYWdlVy8zKi0xLjMsIHkgPSBwYWdlSC8zKi0xLjMzLCBsYWJlbCA9ICJQb3NpdGlvbiIsIHNpemU9MywgaGp1c3QgPSAwKSArCiAgYW5ub3RhdGUoInRleHQiLCB4ID0gcGFnZVcvMyotMS4zLCB5ID0gcGFnZUgvMyotMS40LCBsYWJlbCA9ICJGcmllbmRzIGFyZSBhcnJhbmdlZCBzZXF1ZW50aWFsbHkgaW4gdGhlIG9yZGVyIHRoZSBmYWNlYm9vayBjb25uZWN0aW9ucyB3ZXJlIG1hZGUsIFxuY2xvY2t3aXNlIGZyb20gb2xkZXN0IHRvIG5ld2VzdC4iLCBzaXplPTIuNiwgaGp1c3QgPSAwKSsKICBhbm5vdGF0ZSgidGV4dCIsIHggPSBwYWdlVy8zKi0xLjMsIHkgPSBwYWdlSC8zKi0xLjUsIGxhYmVsID0gIkRpc3RhbmNlIGZyb20gdGhlIGNlbnRyZXBvaW50IGlzIGRldGVybWluZWQgYnkgaG93IHJlcXVlbnRseS9mcmVxdWVudGx5IHRoZSBmcmllbmQgaGFzIFxubWVzc2FnZWQgeW91IG9uIGZhY2Vib29rICh3aXRoIG1vc3QgcmVjZW50L2ZyZXF1ZW50IGNsb3NlciB0byB0aGUgY2VudHJlLikiLCBzaXplPTIuNiwgaGp1c3QgPSAwKSArCiAgYW5ub3RhdGUoInRleHQiLCB4ID0gcGFnZVcvMyotMS4zLCB5ID0gcGFnZUgvMyotMS41OSwgbGFiZWwgPSAiTWV0IHRocm91Z2giLCBzaXplPTMsIGhqdXN0ID0gMCkgKwogIGFubm90YXRlKCJ0ZXh0IiwgeCA9IHBhZ2VXLzMqLTEuMywgeSA9IHBhZ2VILzMqLTEuNzIsIGxhYmVsID0gIkNvbm5lY3Rpb25zIiwgc2l6ZT0zLCBoanVzdCA9IDApIAoKZ2dzYXZlKCJwYXR0ZXJuLnBkZiIsIHdpZHRoID0gcGFnZVcrMSwgIGhlaWdodCA9IHBhZ2VIKzEsICB1bml0cyA9ICJjbSIpICMjIEV4cG9ydCB0aGUgcGF0dGVybiBhcyBhIFBERgoKYGBgCgojIyMgUHJpbnQgeW91ciBwZGYKQmVmb3JlIHByaW50aW5nIHRoZSBwZGYsIHlvdSBtYXkgd2FudCB0byBtYWtlIHNvbWUgdHdlYWtzIHRvIHRoZSBsZWdlbmQsIGRlcGVuZGluZyBvbiB0aGUgaW5mb3JtYXRpb24geW91IHdhbnQgdG8gaW5jbHVkZSBhbmQgaG93IHlvdSB3YW50IGl0IGxhaWQgb3V0LiBUaGUgZWFzaWVzdCB3YXkgdG8gZG8gdGhpcyBpcyBieSBlZGl0aW5nIHRoZSBwZGYgaW4gYSBwZGYgZWRpdG9yLiAKCk9uY2UgeW91J3JlIGhhcHB5IHdpdGggdGhlIHBkZiwgdHJhbnNmZXIgdGhlIHBhdHRlcm4gb250byBmYWJyaWMuIFt3d3cuc3RpdGNoeW91cnNvY2lhbC5uZXR3b3JrXSh3d3cuc3RpdGNoeW91cnNvY2lhbC5uZXR3b3JrKSBoYXMgc29tZSBzdWdnZXN0aW9ucyBmb3IgZGlmZmVyZW50IHdheXMgdG8gdHJhbnNmZXIgeW91ciBwYXR0ZXJuLgoKCiMjIyBFbWJyb2lkZXIKWW91IGNhbiB1c2UgdGhpcyBpbnRlcmFjdGl2ZSByZWZlcmVuY2UgYXMgYSBndWlkZSB3aGlsZSBlbWJyb2lkZXJpbmcgeW91ciBwYXR0ZXJuLiAKCmBgYHtyfQojIyBIYW5kbGUgaWYgZ3JvdXAgY29sb3VycyBhcmVuJ3QgaW4gdXNlIC0gY29tbWVudCBvdXQgaWYgdGhleSBhcmUsIHJlbW92ZSAjIGlmIHRoZXkgYXJlbid0CiNncm91cENvbG91cnMgPC0gYyhncm91cENvbG91cnMsJ05vbmUnPScjN2Q3ZDdkJykKI2ludGVyYWN0aW9ucyA8LSBkcGx5cjo6bXV0YXRlKGludGVyYWN0aW9ucywgYWNyb3NzKGMoZ3JvdXApLCByZXBsYWNlX25hLCAnTm9uZScpKSAjUmVwbGFjZSBibGFuayBncm91cHMgd2l0aCAnbm9uZScKCiNpbnRlcmFjdGlvbnMkbmFtZSA8LSAicmVkYWN0ZWQiICMjVW5jb21tZW50IHRoaXMgbGluZSB0byByZWRhY3QgbmFtZXMgZnJvbSBIVE1MIHNhdmUgaWYgbmVlZGVkLgoKIyMgRHJhdyBpbnRlcmFjdGl2ZSBwbG90CmZpZyA8LSBwbG90X2x5KAogIGludGVyYWN0aW9ucywgeCA9IH54LCB5ID0gfnksIHdpZHRoPTcwMCwgaGVpZ2h0PTcwMCwgdHlwZSA9ICdzY2F0dGVyJywgbW9kZSA9ICdtYXJrZXJzJywgY29sb3IgPSB+Z3JvdXAsIGNvbG9ycyA9IGdyb3VwQ29sb3VycywKICBzeW1ib2wgPSB+aGlnaGxpZ2h0LCBzeW1ib2xzID0gYygiWSI9J2NpcmNsZScsICJOIiA9ICdvJyksCiAgIyBIb3ZlciB0ZXh0OgogIHRleHQgPSB+cGFzdGUoIk5hbWU6ICIsIG5hbWUsIjxicj5NZXQgYmVjYXVzZToiLCBncm91cCwgIjxicj5GaXJzdCBjb25uZWN0ZWQ6IiwgY29ubmVjdGVkRGF0ZSwgIjxicj5Nb3N0IHJlY2VudCBtZXNzYWdlOiIsbW9zdFJlY2VudE1EYXRlLCAiPGJyPiMgbWVzc2FnZXM6IiwgY291bnRNLCAiPGJyPkFubm90YXRpb246IiwgYW5ub3RhdGlvbikpICU+JSBsYXlvdXQoc2hvd2xlZ2VuZCA9IEZBTFNFKQoKZmlnCmBgYApOb3RlOiBUaGUgZmlsbGVkL3N0cm9rZWQgZG90cyBhcmUgaW52ZXJ0ZWQgYmV0d2VlbiB0aGUgcHJpbnQgYW5kIGludGVyYWN0aXZlIHZlcnNpb25zLgoKIyMjIFNoYXJlClBvc3QgYSBwaG90byBvZiB5b3VyIGZpbmlzaGVkIHBpZWNlIHRvIGluc3RhZ3JhbSBvciBmYWNlYm9vayB1c2luZyAgI3N0aXRjaHlvdXJzb2NpYWxuZXR3b3JrCgoK